import * as turf from '@turf/turf';

/**
 * 雨滴效果类，用于在Cesium场景中根据GeoJSON范围创建下雨效果
 */
export default class RainEffect {
  /**
   * 构造函数
   * @param {Cesium.Viewer} viewer - Cesium视图实例
   * @param {Object} options - 雨滴配置参数
   * @param {Object} options.geojson - 下雨区域的GeoJSON对象（必须是Polygon或MultiPolygon类型）
   * @param {number} [options.dx=15] - 雨滴宽度
   * @param {number} [options.ds=5000] - 雨滴数量
   * @param {number} [options.sd=50] - 雨滴下落速度
   * @param {string} [options.image='./yd.jpg'] - 雨滴图片路径
   */0
  constructor(viewer, options = {}) {
    if (!viewer) {
      throw new Error('必须传入Cesium.Viewer实例');
    }
    this.viewer = viewer;
    // 合并默认配置与用户配置
    this.options = {
      dx: 15,
      ds: 100,
      sd: 50,
      image: '/src/assets/img/yd.png',
      ...options
    };

    // 验证必要的GeoJSON参数
    if (!this.options.geojson) {
      throw new Error('缺少必要参数: geojson');
    }
    const validTypes = ['Polygon', 'MultiPolygon'];
    if (!validTypes.includes(this.options.geojson.type)) {
      throw new Error(`geojson类型必须是${validTypes.join('或')}`);
    }

    this.billboardCollection = viewer.scene.primitives.add(new Cesium.BillboardCollection());
    this.rainDrops = [];
    this.init();
  }

  /**
   * 初始化雨滴效果
   */
  init() {
    this.createRainDrops();
    this.startAnimation();
  }

  /**
   * 创建雨滴实例（基于GeoJSON范围）
   */
  createRainDrops() {
    if (typeof turf === 'undefined') {
      throw new Error('请引入turf库以生成随机点');
    }

    // 根据GeoJSON范围生成随机点
    const points = turf.randomPoint(this.options.ds, {
      bbox: turf.bbox(this.options.geojson) // 从GeoJSON计算边界框
    });

    // 过滤出在GeoJSON范围内的点
    const pointsInPolygon = points.features.filter(feature => {
      return turf.booleanPointInPolygon(
        feature,
        this.options.geojson
      );
    });

    // 补充可能被过滤掉的点以保证数量
    let remaining = this.options.ds - pointsInPolygon.length;
    while (remaining > 0) {
      const Points = turf.randomPoint(remaining, {
        bbox: turf.bbox(this.options.geojson)
      });
      Points.features.forEach(feature => {
        if (turf.booleanPointInPolygon(feature, this.options.geojson)) {
          pointsInPolygon.push(feature);
          remaining--;
        }
      });
    }

    // 创建雨滴Billboard
    pointsInPolygon.forEach((feature) => {
      const [lng, lat] = feature.geometry.coordinates;
      const billboard = this.billboardCollection.add({
        scaleByDistance: new Cesium.NearFarScalar(2000000, 0.8, 8000000, 0.3),
        position: Cesium.Cartesian3.fromDegrees(lng, lat, this.getRandomHeight()),
        image: this.options.image,
        width: this.options.dx,
        height: this.options.dx,
      });
      this.rainDrops.push(billboard);
    });
  }

  /**
   * 获取随机高度
   * @returns {number} 随机高度值
   */
  getRandomHeight() {
    return Math.floor(Math.random() * (10000 - 1000 + 1)) + 1000;
  }

  /**
   * 开始雨滴动画
   */
  startAnimation() {
    this.updateHandler = this.updateRainPositions.bind(this);
    this.viewer.scene.postRender.addEventListener(this.updateHandler);
  }

  /**
   * 更新所有雨滴位置
   */
  updateRainPositions() {
    this.rainDrops.forEach(billboard => {
      this.updateBillboardPosition(billboard);
    });
  }

  /**
   * 更新单个雨滴位置
   * @param {Cesium.Billboard} billboard - 雨滴实例
   */
  updateBillboardPosition(billboard) {
    const ellipsoid = this.viewer.scene.globe.ellipsoid;
    const cartographic = ellipsoid.cartesianToCartographic(billboard.position);
    const lat = Cesium.Math.toDegrees(cartographic.latitude);
    const lng = Cesium.Math.toDegrees(cartographic.longitude);
    let height = cartographic.height;

    // 重置或更新高度
    if (height < 0) {
      height = this.getRandomHeight();
    } else {
      height -= this.options.sd;
    }

    billboard.position = Cesium.Cartesian3.fromDegrees(lng, lat, height);
  }

  /**
   * 销毁雨滴效果，释放资源
   */
  destroy() {
    if (this.updateHandler) {
      this.viewer.scene.postRender.removeEventListener(this.updateHandler);
      this.updateHandler = null; // 清除引用
    }
    if (this.billboardCollection && !this.billboardCollection.isDestroyed()) {
      this.viewer.scene.primitives.remove(this.billboardCollection);
      this.billboardCollection.destroy(); // 显式销毁集合
    }
    this.rainDrops = [];
    this.viewer = null;
    this.options = null;
  }
}